<template>
  <div class="bk-input-box bk-selector">
    <input
      v-bk-focus="autoFocus"
      type="text"
      :disabled="disabled"
      :placeholder="placeholder"
      class="bk-form-input"
      autocomplete="off"
      :value="curValue"
      :maxlength="maxlength"
      @focus="focusHandler"
      @blur="blurHandler"
      @input="userInput"
      @keyup="keyup"
      @paste="paste"
    >
    <transition :name="listSlideName">
      <div
        v-show="isListPanelShow && resultList.length"
        class="bk-selector-list"
        :style="panelStyle"
      >
        <ul class="selector-list-box">
          <li
            v-for="(item, index) of resultList"
            :key="index"
            class="bk-selector-list-item selected"
            @click="confirmSelect($event, index)"
          >
            <div :class="['bk-selector-node', { 'bk-selector-selected': item.isSelected }]">
              <span class="text">{{ item[displayKey] }}</span>
            </div>
          </li>
        </ul>
      </div>
    </transition>
  </div>
</template>

<script>
    import timer from '@/common/debounce';
    import Vue from 'vue';

    Vue.directive('bk-focus', {
        inserted: function (el, binding) {
            if (binding.value) {
                el.focus();
            }
        },
        update (el, binding) {
            binding.value && el.focus();
        }
    });

    /**
     *  获取元素相对于页面的高度
     *  @param node {NodeElement} 指定的DOM元素
     */
    export function getActualTop (node) {
        let actualTop = node.offsetTop;
        let current = node.offsetParent;

        while (current !== null) {
            actualTop += current.offsetTop;
            current = current.offsetParent;
        }

        return actualTop;
    }

    /**
     *  获取元素相对于页面左侧的宽度
     *  @param node {NodeElement} 指定的DOM元素
     */
    export function getActualLeft (node) {
        let actualLeft = node.offsetLeft;
        let current = node.offsetParent;

        while (current !== null) {
            actualLeft += current.offsetLeft;
            current = current.offsetParent;
        }

        return actualLeft;
    }

    export default {
        props: {
            type: {
                type: String,
                default: 'text' // text || number
            },
            isDecimals: {
                type: Boolean,
                default: false
            },
            isSelectMode: {
                type: Boolean,
                default: false
            },
            value: {
                type: [Number, String],
                default: ''
            },
            placeholder: {
                type: String,
                default: ''
            },
            useChinese: {
                type: Boolean,
                default: true
            },
            autoFocus: {
                type: Boolean,
                default: false
            },
            disabled: {
                type: [String, Boolean],
                default: false
            },
            regexp: {
                type: Object
            },
            maxlength: {
                type: [Number, String]
            },
            min: {
                type: [Number, String],
                default: Number.NEGATIVE_INFINITY
            },
            max: {
                type: [Number, String],
                default: Number.POSITIVE_INFINITY
            },
            isLink: {
                type: Boolean,
                default: false
            },
            steps: {
                type: Number,
                default: 1
            },
            size: {
                type: String,
                default: 'large',
                validator (value) {
                    return [
                        'large',
                        'small'
                    ].indexOf(value) > -1;
                }
            },
            debounceTimer: {
                type: Number,
                default: 100
            },
            searchKey: {
                type: String,
                default: 'key'
            },
            settingKey: {
                type: String,
                default: 'key'
            },
            displayKey: {
                type: String,
                default: 'key'
            },
            list: {
                type: Array,
                default () {
                    return [];
                }
            },
            defaultList: {
                type: Array,
                default () {
                    return null;
                }
            }
        },
        data () {
            return {
                inputMode: 'input', // search || input
                isSearch: false,
                isListPanelShow: false,
                searchPrefix: '{{',
                searchSuffix: '}}',
                searchReg: /\{\{([^\{\}]+)?\}\}/,
                chineseReg: /[\u4e00-\u9fa5]/g,
                keyWord: '',
                curSelectIndex: -1,
                resultList: this.defaultList || this.list,
                timer: 0,

                isMax: false,
                isMin: false,
                curValue: '',
                isFocus: false,
                maxNumber: this.max,
                minNumber: this.min,
                panelStyle: {},
                listSlideName: 'toggle-slide'
            };
        },
        watch: {
            min () {
                this.minNumber = Number(this.min);
            },
            max () {
                this.maxNumber = Number(this.max);
            },
            value: {
                immediate: true,
                handler (value) {
                    this.changeCurValue(this.isLink);
                }
            },
            defaultList: {
                immediate: true,
                handler (value) {
                    this.changeCurValue(this.isLink);
                }
            },
            list: {
                immediate: true,
                handler (value) {
                    this.changeCurValue(this.isLink);
                }
            },
            keyWord (val) {
                const searchKey = this.searchKey;
                let sourceList = [];
                let targetList = [];
                let keyWord = val;
                if (this.inputMode === 'input' && this.defaultList) {
                    sourceList = this.defaultList;
                    targetList = this.defaultList;
                } else if (this.inputMode === 'search') {
                    sourceList = this.list;
                    targetList = this.list;
                    keyWord = this.getKeyWord(val);
                }

                if (keyWord) {
                    const key = keyWord.toLowerCase();
                    targetList = sourceList.filter(item => {
                        return item[searchKey] && String(item[searchKey]).toLowerCase().indexOf(key) > -1;
                    });
                }

                targetList.forEach(item => {
                    item.isSelected = false;
                });
                this.curSelectIndex = -1;
                this.resultList = JSON.parse(JSON.stringify(targetList));
            }
        },
        mounted () {
            this.initInputLayout();
            this.numberInput = timer.debounce(this.debounceTimer, event => {
                const value = event.target.value;
                this.numberInputHandler(value, event.target);
            });
        },
        methods: {
            dispatch (componentName, eventName, params) {
                let parent = this.$parent || this.$root;
                let name = parent.$options.name;

                while (parent && (!name || name !== componentName)) {
                    parent = parent.$parent;

                    if (parent) {
                        name = parent.$options.name;
                    }
                }
                if (parent) {
                    parent.$emit.apply(parent, [eventName].concat(params));
                }
            },
            changeCurValue (isTrigger) {
                const value = this.value + '';
                // 如果是选择模式，从列表项匹配
                if (this.isSelectMode) {
                    const selectItem = this.getItemByKey(this.value);
                    if (selectItem) {
                        if (selectItem.type === 'variable') {
                            this.curValue = '{{' + selectItem[this.displayKey] + '}}';
                        } else {
                            this.curValue = selectItem[this.displayKey];
                        }

                        this.$emit('item-selected', value, selectItem, isTrigger);
                    } else {
                        this.curValue = '';
                    }
                    return;
                }
                if (value === '' || value.startsWith(this.searchPrefix)) {
                    this.curValue = value;
                    return;
                }
                this.curValue = value;
            },
            paste (event) {
                this.$emit('paste', event);
            },
            keyup (event) {
                const code = event.code;
                switch (code) {
                    case 'ArrowDown':
                        if (this.type === 'number' && this.inputMode === 'input') {
                            this.minus();
                        } else {
                            this.selectNextItem();
                        }
                        break;
                    case 'ArrowUp':
                        if (this.type === 'number' && this.inputMode === 'input') {
                            this.add();
                        } else {
                            this.selectPrevItem();
                        }
                        break;
                    case 'Enter':
                        if (this.inputMode === 'search' || this.defaultList) {
                            this.confirmSelect(event);
                        } else {
                            this.$emit('blur', event);
                        }
                        break;
                }
            },
            selectNextItem () {
                this.curSelectIndex = this.curSelectIndex + 1;
                if (this.curSelectIndex >= this.resultList.length) {
                    this.curSelectIndex = this.resultList.length - 1;
                }

                this.selectItemByIndex(this.curSelectIndex);
            },
            selectPrevItem () {
                this.curSelectIndex = this.curSelectIndex - 1;
                if (this.curSelectIndex < 0) {
                    this.curSelectIndex = 0;
                }
                this.selectItemByIndex(this.curSelectIndex);
            },
            selectItemByIndex (index) {
                this.resultList.forEach((item, i) => {
                    if (i === index) {
                        item.isSelected = true;
                    } else {
                        item.isSelected = false;
                    }
                });

                this.resultList = JSON.parse(JSON.stringify(this.resultList));

                this.setScrollTop();
            },
            getItemByKey (key) {
                let selectItem = null;

                for (const item of this.defaultList) {
                    if (item[this.settingKey] === key) {
                        selectItem = item;
                        selectItem.type = 'normal';
                    }
                }

                for (const item of this.list) {
                    if (`{{${item[this.settingKey]}}}` === key) {
                        selectItem = item;
                        selectItem.type = 'variable';
                    }
                }

                return selectItem;
            },
            selectItemByKey (key) {
                if (!key) {
                    this.curSelectIndex = -1;
                }
                this.resultList.forEach((item, index) => {
                    if (item[this.settingKey] === key) {
                        item.isSelected = true;
                        this.curSelectIndex = index;
                    } else {
                        item.isSelected = false;
                    }
                });

                this.resultList = JSON.parse(JSON.stringify(this.resultList));

                this.setScrollTop();
            },
            setScrollTop () {
                const MAX_SHOW_NUM = 3;
                const LIST_ITEM_HEIGHT = 42;

                if (this.curSelectIndex > MAX_SHOW_NUM) {
                    const offset = this.curSelectIndex - MAX_SHOW_NUM;
                    const scrollTop = offset * LIST_ITEM_HEIGHT;
                    this.$el.querySelector('.selector-list-box').scrollTop = scrollTop;
                }
            },
            confirmSelect (event, index) {
                if (index !== undefined) {
                    this.curSelectIndex = index;
                }
                if (this.resultList[this.curSelectIndex]) {
                    const selectItem = this.resultList[this.curSelectIndex];
                    const val = selectItem[this.settingKey];
                    const text = selectItem[this.displayKey];
                    if (val) {
                        let inputText = '';
                        let inputVal = '';
                        let preVal = '';
                        let newVal = '';
                        if (this.inputMode === 'search') {
                            inputText = `${this.searchPrefix}${text}${this.searchSuffix}`;
                            inputVal = `${this.searchPrefix}${val}${this.searchSuffix}`;
                            preVal = this.value;
                            newVal = inputVal;
                            if (preVal && this.searchReg.exec(preVal)) {
                                newVal = preVal.replace(this.searchReg, inputVal);
                            }
                        } else {
                            inputText = `${text}`;
                            inputVal = `${val}`;
                            preVal = this.value;
                            newVal = inputVal;
                            // if (preVal && this.searchReg.exec(preVal)) {
                            //     newVal = preVal.replace(this.searchReg, inputVal)
                            // }
                        }

                        event.target.value = inputText;
                        this.curValue = inputText;
                        // alert(inputText)
                        this.$emit('update:value', newVal);
                        this.$emit('input', newVal);
                        this.$emit('item-selected', newVal, selectItem, false);
                        this.hideListPanel();
                        this.inputMode = 'input';
                    }
                }
            },
            getPower (val) {
                const valueString = val.toString();
                const dotPosition = valueString.indexOf('.');

                let power = 0;
                if (dotPosition > -1) {
                    power = valueString.length - dotPosition - 1;
                }
                return Math.pow(10, power);
            },
            checkMinMax (val) {
                if (val <= this.minNumber) {
                    val = this.minNumber;
                    this.isMin = true;
                } else {
                    this.isMin = false;
                }
                if (val >= this.maxNumber) {
                    val = this.maxNumber;
                    this.isMax = true;
                } else {
                    this.isMax = false;
                }
                return val;
            },
            textInput (val, event) {
                if (val.startsWith(this.searchPrefix)) {
                    this.inputMode = 'search';
                    this.keyWord = val;
                    this.showListPanel(event);
                } else {
                    this.inputMode = 'input';
                    this.keyWord = '';
                    if (this.defaultList) {
                        this.keyWord = val;
                        this.showListPanel(event);
                    } else {
                        this.hideListPanel();
                    }
                }
                this.curValue = val;
                this.$emit('change', val);
                this.$emit('input', val);
                // 如果不是选择模式，直接修改值
                if (!this.isSelectMode) {
                    this.$emit('update:value', val);
                }
            },
            userInput (event) {
                let val = event.target.value;

                if (!this.useChinese) {
                    val = val.replace(this.chineseReg, '');
                }
                if (this.type === 'number' && val && this.searchReg.exec(val)) {
                    const match = this.searchReg.exec(val);
                    val = match[0];
                    event.target.value = val;
                }
                if (this.type === 'number') {
                    if (val === '{') {
                        this.curValue = val;
                        this.hideListPanel();
                        return;
                    }

                    if (val.startsWith(this.searchPrefix)) {
                        this.inputMode = 'search';
                        this.keyWord = val;
                        this.showListPanel(event);
                        this.curValue = val;
                        this.$emit('change', val);
                        this.$emit('input', val);
                        return;
                    }

                    this.inputMode = 'input';
                    this.numberInput(event);
                } else {
                    this.textInput(val, event);
                }
            },
            numberInputHandler (value, target) {
                if (value === '') {
                    this.$emit('update:value', value);
                    this.$emit('change', value);
                    this.curValue = value;
                    target && (target.value = value);
                    return;
                }
                if (value !== '' && value.indexOf('.') === (value.length - 1)) {
                    return;
                }

                if (value !== '' && value.indexOf('.') > -1 && Number(value) === 0) {
                    return;
                }

                let newVal = parseInt(value);

                if (this.isDecimals) {
                    newVal = Number(value);
                }

                if (!isNaN(newVal)) {
                    this.setNumberValue(newVal, target);
                } else {
                    target.value = this.curValue;
                }
            },
            setNumberValue (val, target) {
                val = this.checkMinMax(val);
                this.$emit('update:value', val);
                this.$emit('change', val);
                this.$emit('input', val);
                this.curValue = val;
                target && (target.value = val);
            },
            add () {
                if (this.disabled) return;
                const value = this.value || 0;
                if (typeof value !== 'number') return this.curValue;
                const power = this.getPower(value);
                const newVal = (power * value + power * this.steps) / power;
                if (newVal > this.max) return;
                this.setNumberValue(newVal);
            },
            minus () {
                if (this.disabled) return;
                const value = this.value || 0;
                if (typeof value !== 'number') return this.curValue;
                const power = this.getPower(value);
                const newVal = parseInt(power * value - power * this.steps) / power;
                if (newVal < this.min) return;
                this.setNumberValue(newVal);
            },
            getKeyWord (val) {
                val = val + '';
                let keyWord = '';
                if (val.startsWith(this.searchPrefix)) {
                    const startIndex = this.searchPrefix.length;
                    let endIndex = val.length;
                    if (val.endsWith(this.searchSuffix)) {
                        endIndex = val.length - this.searchSuffix.length;
                    }

                    keyWord = val.substring(startIndex, endIndex);
                } else if (this.searchReg.exec(val)) {
                    const match = this.searchReg.exec(val);
                    if (match && match[1]) {
                        keyWord = match[1];
                    }
                } else {
                    keyWord = '';
                }

                return keyWord;
            },
            initInputLayout () {
                // const element = this.$el
            },
            showListPanel (event) {
                if (this.disabled) {
                    return;
                }
                this.initSelectorPosition(event.currentTarget);
                clearTimeout(this.timer);
                this.isListPanelShow = true;
            },
            hideListPanel () {
                this.timer = setTimeout(() => {
                    this.isListPanelShow = false;
                }, 200);
            },
            focusHandler (event) {
                if (!this.isSelectMode) {
                    event.target.select();
                }

                const val = this.value + '';
                const key = this.getKeyWord(val);
                if (key) {
                    this.inputMode = 'search';
                    const list = JSON.parse(JSON.stringify(this.list));

                    list.forEach(item => {
                        item.isSelected = false;
                    });

                    this.resultList = list;
                    this.selectItemByKey(key);
                    this.setScrollTop();
                    this.showListPanel(event);
                } else {
                    this.inputMode = 'input';
                    // 如果有下拉列表，在输入模式显示下拉列表
                    if (this.defaultList && this.defaultList) {
                        const list = JSON.parse(JSON.stringify(this.defaultList));
                        list.forEach(item => {
                            item.isSelected = false;
                        });

                        this.resultList = list;
                        this.selectItemByKey(val);
                        this.setScrollTop();
                        this.showListPanel(event);
                    } else {
                        this.resultList = [];
                    }
                }
                this.$emit('focus', event);
                this.dispatch('bk-form-item', 'form-focus');
            },
            blurHandler (event) {
                if (this.type === 'number') {
                    this.curValue = this.value;
                } else if (this.isSelectMode) {
                    const selectItem = this.getItemByKey(this.value);
                    if (selectItem) {
                        if (selectItem.type === 'variable') {
                            this.curValue = '{{' + selectItem[this.displayKey] + '}}';
                        } else {
                            this.curValue = selectItem[this.displayKey];
                        }
                    } else {
                        this.curValue = '';
                    }
                }
                this.hideListPanel();
                this.timer = setTimeout(() => {
                    this.$emit('blur', event);
                    this.dispatch('bk-form-item', 'form-blur');
                }, 200);
            },
            initSelectorPosition (currentTarget) {
                if (currentTarget) {
                    const distanceTop = getActualTop(currentTarget);
                    const winHeight = document.body.clientHeight;
                    let ySet = {};
                    let listHeight = this.list.length * 42;
                    if (listHeight > 160) {
                        listHeight = 160;
                    }
                    const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

                    if ((distanceTop + listHeight + 42 - scrollTop) < winHeight) {
                        ySet = {
                            top: '40px',
                            bottom: 'auto'
                        };

                        this.listSlideName = 'toggle-slide';
                    } else {
                        ySet = {
                            top: 'auto',
                            bottom: '40px'
                        };

                        this.listSlideName = 'toggle-slide2';
                    }

                    this.panelStyle = { ...ySet };
                }
            }
        }
    };
</script>
<style lang="scss" scoped>
    .bk-input-box {
        position: relative;
        .bk-form-input {
            width: 100%;
        }
    }
    .bk-input-result {
        position: absolute;
        top: 40px;
        left: 0;
        right: 0;
        box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1);
        background-color: #fff;
        z-index: 100;
        overflow-y: hidden;
        ul {
            max-height: 169px !important;
            padding: 0;
            margin: 0;
            list-style: none;
            overflow-y: auto;
        }
        li {
            position: relative;
            width: 100%;
            border-left: #c3cdd7;
            border-right: #c3cdd7;
            background-color: #fff;
            cursor: pointer;
            .text {
                padding: 0 10px;
                line-height: 42px;
                font-size: 14px;
                display: block;
                text-align: left;
            }
        }
    }
    .bk-selector-list {
        min-width: 150px;
    }
</style>
